今天要做什麼
把一個資料夾(例如 Download)裡的檔案,自動整理到:
目標資料夾/
2025-07/
pdf/
jpg/
2025-08/
docx/
png/
預設 move(搬移,保留時間戳與檔名)
可用 --mode copy 改成複製
可用 --dry 先試跑不動檔
會用到
pathlib 檔案系統
shutil 複製/搬移
datetime 取檔案時間
argparse 命令列參數
程式碼(organize.py)
把下面存成 organize.py 放在你的 project 資料夾。
from pathlib import Path
import argparse, shutil, datetime as dt, sys, os
def month_bucket(p: Path) -> str:
"""依檔案最後修改時間決定月份資料夾名稱,例如 2025-09"""
ts = dt.datetime.fromtimestamp(p.stat().st_mtime)
return f"{ts:%Y-%m}"
def is_hidden(p: Path) -> bool:
# 排除隱藏/系統檔(跨平台簡易判斷)
name = p.name
if name.startswith("."): # mac/Linux 隱藏檔
return True
# Windows: 用 FILE_ATTRIBUTE_HIDDEN(簡化:跳過常見 OneDrive 暫存)
return name in ("desktop.ini", "Thumbs.db")
def unique_target_path(target_dir: Path, name: str) -> Path:
"""避免同名覆蓋:若已存在,加入 (1), (2)..."""
base, dot, ext = name.partition(".")
candidate = target_dir / name
i = 1
while candidate.exists():
candidate = target_dir / (f"{base} ({i})" + (dot + ext if dot else ""))
i += 1
return candidate
def organize(src: Path, dst: Path, mode: str, dry: bool):
if not src.exists():
sys.exit(f"來源不存在:{src}")
if not src.is_dir():
sys.exit("來源必須是資料夾")
count = 0
for f in src.rglob("*"):
if not f.is_file():
continue
if is_hidden(f):
continue
month = month_bucket(f)
ext = f.suffix.lower().lstrip(".") or "noext"
target_dir = dst / month / ext
target_dir.mkdir(parents=True, exist_ok=True)
target_path = unique_target_path(target_dir, f.name)
rel = f.relative_to(src)
if dry:
print(f"[DRY] {rel} -> {target_path.relative_to(dst)}")
else:
if mode == "move":
shutil.move(str(f), target_path)
else:
shutil.copy2(str(f), target_path)
count += 1
if dry:
print("(乾跑模式)不做任何實際搬移/複製。")
else:
print(f"✅ 完成,處理檔案數:{count}")
def main():
ap = argparse.ArgumentParser(description="檔案批次整理器:依月份與副檔名分類")
ap.add_argument("--src", required=True, help="來源資料夾")
ap.add_argument("--dst", required=True, help="目標資料夾")
ap.add_argument("--mode", choices=["move", "copy"], default="move", help="move=搬移(預設),copy=複製")
ap.add_argument("--dry", action="store_true", help="乾跑模式(只顯示要做什麼,不動檔)")
args = ap.parse_args()
src = Path(args.src).expanduser()
dst = Path(args.dst).expanduser()
organize(src, dst, args.mode, args.dry)
if __name__ == "__main__":
main()
執行範例
先試跑(不動檔)python organize.py --src "C:\Users\anna4\Downloads" --dst "C:\Users\anna4\Projects\Media" --dry
真的搬移
python organize.py --src "C:\Users\anna4\Downloads" --dst "C:\Users\anna4\Projects\Media"
改成複製python organize.py --src "C:\Users\anna4\Downloads" --dst "C:\Users\anna4\Projects\Media" --mode copy
實作:
今日重點
pathlib 讓走訪檔案結構更直覺
依stat().st_mtime 取得最後修改時間分桶
碰到同名檔,動態加 (1)(2)… 避免覆蓋
先 dry-run 再正式動作,安全不踩雷
明天Day 4我們會做一個小型爬蟲,抓一個公開網站的標題與連結(含合法爬蟲禮節與限速)。